package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// 在Go1.7发布时，标准库增加了一个context包，
// 用来简化对于处理单个请求的多个Goroutine之间与请求域的数据、
// 超时和退出等操作，官方有博文对此做了专门介绍。
// 我们可以用context包来重新实现前面的线程安全退出或超时的控制:
// 而且还有减少内存泄漏

func worker(wg *sync.WaitGroup, ctx context.Context, n int) error {
	defer wg.Done()
	fmt.Println("开启线程>>>>", n)

	for {
		select {
		default:
			fmt.Println("hello")
			time.Sleep(1000 * time.Microsecond)
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		time.Sleep(1000 * time.Millisecond)
		wg.Add(1)
		go worker(&wg, ctx, i)
	}
	time.Sleep(time.Second)
	cancel()
	wg.Wait()
}

// 总结goroutine
// 关于goroutine 并非正式的线程，而是一种虚拟线程，没开启一个根据系统有关，占的内存为4-8k
// 所有可以轻松的开启上万个goroutine
// 关于并发安全的问题, 可以通过go run -race 文件来检测，go 内置的并发检测，
// 当然还要写go 内置的调优工具，doc工具等等
// 接线来说并发安全的问题
// 为什么会有并发?
// 由于cpu可以通过切换不同的线层来完成不同量级的任务，表面上看是并行实际上切是并发，在同一时间只有一条线程在做事情
// 那么如果能够让cpu充分的利用起来呢？
// 这个问题跟GMP调度有关了，g就是gorountine了，p 就是调度G<-->M之间的协调，M machine是真正做事的
// M的数量取决于并行任务的数量。p就是从线程队列里面调度不同的队列完成不同的任务，任务堵塞的时候回选择性的切换任务
// 同时把堵塞或者有问题的线程放在global channel里面等待系统检测再次的执行
// 关于抢资源的问题？
// 由于不同的G在同一时刻去操作同一个共享的变量或者说内存的时候，就会产生资源抢夺的问题，当然Goroutine内部的执行或者说
// goroutine是安全的，但是被争抢的变量操作结果是不安全的，如果解决问题呢？
// 其实万变不离其中，都是保证让线程能够按照自己的一定顺序去执行，系统调度是无序的，那么只需把调度的顺序按照一定的顺序执行
// 就可以解决上面说的争抢问题
// 那么用什么办法解决呢？
// 1. 锁机制或者读写锁<该锁的使用场景，还要根据需求来使用，避免在修改的时候无法读取的问题>，
// 2. 管道来当然是一个无缓冲的管道。来规定goroutine执行的顺序
// 3. context 来实现这个就比较牛逼了，而且保证线程安全退出，减少内存泄漏，内置的功能，推荐使用 <关于机制，就是类似管道>
// 4. 其他很多
// 那么顺序有了，但是还是无法保证数据的原子性,那么该怎么办呢？
// 那么可以使用一些内置的方法或者锁机制，如上述的问题一样，结果都是为了处理并发安全的问题,
// 上述的方法都能解决原子问题，那么要更优的去解决的话，需要用到内置的一些原子方法，atomic 来实现原子操作
// 如果是DB或者是NOSQL的话，实现的方法如下
// 1.可以使用 事务<保证原子性要么都更新要么都回滚> ,当然同时也需要一个watch监听事件，保证在更新的同时对应更新key的value
// 没有发生变化，否则需要回滚再次提交
// 2. 使用生产消费者模式，把无序的生产者变成有序的信号，消费者从channel中，获取有序的信息然后一一的处理.
// 举例，比如每一个post提交<用户的下单请求当做一个生产者产生的消息>--存到channel中-->对应的消费者服务采取消费即可
// 好处又是可伸缩，可拓展，并发较高，高吞吐的设计模式
// 3.如果我不想设计上面的模式咋办，OK那么由开源的第三方工具，比如nsq,MQ,kafka等这中设计模式已经成熟，可以参考文档来使用
// 关于比较骚气的容器间搭配的操作
// 1. map[chan] func()
// 2. map[func()] func()
// 3. map[interfac{}] interfac{}
// 4. map[struct] struct 这些不同的搭配，为了就是更好的动态设计模式，关于更多的搭配还需从经验中慢慢的摸索，
// 切勿为了使用而去搭配使用，应该是根据自己的场景和需要来使用
// 关于23中的设计模式 同样更上使用一样的道理
// 关于结构的运用
// 需要经验的累计，这个跟设计模式息息相关
